﻿namespace Microsoft.SharePoint.Client
{
    using System;
    using System.Collections.Generic;
    using System.Linq.Expressions;
    using System.Reflection;
    using System.Xml;

    internal class ConditionalExpressionToXmlConverter
    {
        private Dictionary<string, Type> m_allowedParameters;
        private Expression m_condition;
        private SerializationContext m_serializationContext;
        private XmlWriter m_writer;
        private static Dictionary<ExpressionType, string> s_opNames = new Dictionary<ExpressionType, string>();

        static ConditionalExpressionToXmlConverter()
        {
            s_opNames[ExpressionType.GreaterThan] = "GT";
            s_opNames[ExpressionType.LessThan] = "LT";
            s_opNames[ExpressionType.Equal] = "EQ";
            s_opNames[ExpressionType.NotEqual] = "NE";
            s_opNames[ExpressionType.GreaterThanOrEqual] = "GE";
            s_opNames[ExpressionType.LessThanOrEqual] = "LE";
            s_opNames[ExpressionType.AndAlso] = "AND";
            s_opNames[ExpressionType.OrElse] = "OR";
            s_opNames[ExpressionType.Not] = "NOT";
        }

        public ConditionalExpressionToXmlConverter(Expression condition, XmlWriter writer, Dictionary<string, Type> allowedParameters, SerializationContext serializationContext)
        {
            this.m_condition = condition;
            this.m_writer = writer;
            this.m_allowedParameters = allowedParameters;
            this.m_serializationContext = serializationContext;
        }

        private static bool CanBeEvaluated(Expression exp)
        {
            if (exp == null)
            {
                return true;
            }
            bool flag = true;
            if (exp.NodeType == ExpressionType.MemberAccess)
            {
                MemberExpression memExp = (MemberExpression) exp;
                if (Attribute.IsDefined(memExp.Member, typeof(RemoteAttribute)) || Attribute.IsDefined(memExp.Member, typeof(PseudoRemoteAttribute)))
                {
                    return false;
                }
                if (IsClientResultValueExpression(memExp) || IsServerObjectIsNullValue(memExp))
                {
                    flag = false;
                }
                return flag;
            }
            if (exp.NodeType == ExpressionType.Call)
            {
                MethodCallExpression expression2 = (MethodCallExpression) exp;
                if (Attribute.IsDefined(expression2.Method, typeof(RemoteAttribute)) || Attribute.IsDefined(expression2.Method, typeof(PseudoRemoteAttribute)))
                {
                    flag = false;
                }
                return flag;
            }
            if (exp.NodeType == ExpressionType.Parameter)
            {
                flag = false;
            }
            return flag;
        }

        public void Convert()
        {
            Expression condition = this.m_condition;
            NotSupportedExpressionChecker checker = new NotSupportedExpressionChecker(this.m_allowedParameters);
            condition = ExpressionEvaluator.PartialEvaluate(checker.Visit(condition), new Func<Expression, bool>(ConditionalExpressionToXmlConverter.CanBeEvaluated));
            this.Write(condition);
        }

        private static string GetMemberName(MemberInfo m)
        {
            RemoteAttribute customAttribute = (RemoteAttribute) Attribute.GetCustomAttribute(m, typeof(RemoteAttribute));
            if ((customAttribute != null) && !string.IsNullOrEmpty(customAttribute.Name))
            {
                return customAttribute.Name;
            }
            PseudoRemoteAttribute attribute2 = (PseudoRemoteAttribute) Attribute.GetCustomAttribute(m, typeof(PseudoRemoteAttribute));
            if ((attribute2 != null) && !string.IsNullOrEmpty(attribute2.Name))
            {
                return attribute2.Name;
            }
            return m.Name;
        }

        private static string GetServerTypeId(Type type)
        {
            ScriptTypeAttribute customAttribute = (ScriptTypeAttribute) Attribute.GetCustomAttribute(type, typeof(ScriptTypeAttribute));
            if (customAttribute == null)
            {
                return null;
            }
            return customAttribute.ServerTypeId;
        }

        private static bool IsClientResultValueExpression(MemberExpression memExp)
        {
            return (((((memExp.Expression != null) && memExp.Expression.Type.IsGenericType) && ((memExp.Expression.Type.GetGenericTypeDefinition() == typeof(ClientResult<bool>).GetGenericTypeDefinition()) || (memExp.Expression.Type.GetGenericTypeDefinition() == typeof(ClientArrayResult<bool>).GetGenericTypeDefinition()))) && (memExp.Member != null)) && (memExp.Member.Name == "Value"));
        }

        private static bool IsNullExpression(Expression exp)
        {
            if (exp.NodeType == ExpressionType.Constant)
            {
                ConstantExpression expression = (ConstantExpression) exp;
                return (expression.Value == null);
            }
            if (exp.NodeType == ExpressionType.Convert)
            {
                UnaryExpression expression2 = (UnaryExpression) exp;
                return IsNullExpression(expression2.Operand);
            }
            return false;
        }

        private static bool IsServerObjectIsNullValue(MemberExpression memExp)
        {
            if ((((memExp.Expression != null) && (memExp.Expression.Type == typeof(bool?))) && ((memExp.Member != null) && (memExp.Member.Name == "Value"))) && (memExp.Expression.NodeType == ExpressionType.MemberAccess))
            {
                MemberExpression expression = (MemberExpression) memExp.Expression;
                if (((expression.Expression != null) && typeof(ClientObject).IsAssignableFrom(expression.Expression.Type)) && ((expression.Member != null) && (expression.Member.Name == "ServerObjectIsNull")))
                {
                    return true;
                }
            }
            return false;
        }

        private static bool IsSupportedEqualType(Type type)
        {
            if ((!type.IsPrimitive && !type.IsEnum) && ((type != typeof(Guid)) && (type != typeof(DateTime))))
            {
                return (type == typeof(string));
            }
            return true;
        }

        private void Write(Expression exp)
        {
            bool flag;
            string str;
            UnaryExpression expression4;
            switch (exp.NodeType)
            {
                case ExpressionType.Call:
                {
                    MethodCallExpression expression6 = (MethodCallExpression) exp;
                    bool flag2 = false;
                    if (expression6.Object == null)
                    {
                        flag2 = true;
                        this.m_writer.WriteStartElement("ExpressionStaticMethod");
                        string serverTypeId = GetServerTypeId(expression6.Method.DeclaringType);
                        if (string.IsNullOrEmpty(serverTypeId))
                        {
                            throw new ClientRequestException(Resources.GetString("NotSupportedExpression", new object[] { this.m_condition.ToString() }));
                        }
                        this.m_writer.WriteAttributeString("TypeId", serverTypeId);
                        this.m_writer.WriteAttributeString("Name", GetMemberName(expression6.Method));
                    }
                    else
                    {
                        this.m_writer.WriteStartElement("ExpressionMethod");
                        this.m_writer.WriteAttributeString("Name", GetMemberName(expression6.Method));
                        this.Write(expression6.Object);
                    }
                    this.m_writer.WriteStartElement("Parameters");
                    foreach (Expression expression7 in expression6.Arguments)
                    {
                        if (flag2)
                        {
                            flag2 = false;
                        }
                        else
                        {
                            this.Write(expression7);
                        }
                    }
                    this.m_writer.WriteEndElement();
                    this.m_writer.WriteEndElement();
                    return;
                }
                case ExpressionType.Constant:
                {
                    ConstantExpression expression8 = (ConstantExpression) exp;
                    object objValue = expression8.Value;
                    if ((((objValue != null) && !(objValue is ClientObject)) && (!(objValue is ClientValueObject) && !objValue.GetType().IsPrimitive)) && ((!objValue.GetType().IsEnum && (objValue.GetType() != typeof(Guid))) && ((objValue.GetType() != typeof(DateTime)) && (objValue.GetType() != typeof(string)))))
                    {
                        throw new ClientRequestException(Resources.GetString("NotSupportedExpression", new object[] { this.m_condition.ToString() }));
                    }
                    this.m_writer.WriteStartElement("ExpressionConstant");
                    DataConvert.WriteValueToXmlElement(this.m_writer, objValue, this.m_serializationContext);
                    this.m_writer.WriteEndElement();
                    return;
                }
                case ExpressionType.Convert:
                    flag = false;
                    str = null;
                    expression4 = (UnaryExpression) exp;
                    if (expression4.Operand.Type != typeof(object))
                    {
                        if ((expression4.Type.IsPrimitive || expression4.Type.IsEnum) || ((expression4.Type == typeof(string)) || (expression4.Type == typeof(DateTime))))
                        {
                            flag = true;
                            if (expression4.Type.IsEnum)
                            {
                                str = DataConvert.GetTypeName(Enum.GetUnderlyingType(expression4.Type));
                            }
                            else
                            {
                                str = DataConvert.GetTypeName(expression4.Type);
                            }
                        }
                        else if (typeof(ClientObject).IsAssignableFrom(expression4.Type))
                        {
                            flag = true;
                        }
                        break;
                    }
                    flag = true;
                    break;

                case ExpressionType.Equal:
                case ExpressionType.NotEqual:
                {
                    BinaryExpression expression2 = (BinaryExpression) exp;
                    if ((!IsNullExpression(expression2.Left) && !IsNullExpression(expression2.Right)) && (!IsSupportedEqualType(expression2.Right.Type) || !IsSupportedEqualType(expression2.Left.Type)))
                    {
                        throw new ClientRequestException(Resources.GetString("NotSupportedExpression", new object[] { this.m_condition.ToString() }));
                    }
                    this.m_writer.WriteStartElement(s_opNames[exp.NodeType]);
                    this.Write(expression2.Left);
                    this.Write(expression2.Right);
                    this.m_writer.WriteEndElement();
                    return;
                }
                case ExpressionType.GreaterThan:
                case ExpressionType.GreaterThanOrEqual:
                case ExpressionType.AndAlso:
                case ExpressionType.LessThan:
                case ExpressionType.LessThanOrEqual:
                case ExpressionType.OrElse:
                {
                    BinaryExpression expression = (BinaryExpression) exp;
                    this.m_writer.WriteStartElement(s_opNames[exp.NodeType]);
                    this.Write(expression.Left);
                    this.Write(expression.Right);
                    this.m_writer.WriteEndElement();
                    return;
                }
                case ExpressionType.MemberAccess:
                {
                    MemberExpression memExp = (MemberExpression) exp;
                    if (!(memExp.Member is PropertyInfo))
                    {
                        throw new ClientRequestException(Resources.GetString("NotSupportedExpression", new object[] { this.m_condition.ToString() }));
                    }
                    if (!IsClientResultValueExpression(memExp))
                    {
                        if (IsServerObjectIsNullValue(memExp))
                        {
                            this.Write(memExp.Expression);
                            return;
                        }
                        if (memExp.Expression == null)
                        {
                            this.m_writer.WriteStartElement("ExpressionStaticProperty");
                            string str2 = GetServerTypeId(memExp.Member.DeclaringType);
                            if (string.IsNullOrEmpty(str2))
                            {
                                throw new ClientRequestException(Resources.GetString("NotSupportedExpression", new object[] { this.m_condition.ToString() }));
                            }
                            this.m_writer.WriteAttributeString("TypeId", str2);
                            this.m_writer.WriteAttributeString("Name", GetMemberName(memExp.Member));
                            this.m_writer.WriteEndElement();
                            return;
                        }
                        this.m_writer.WriteStartElement("ExpressionProperty");
                        this.m_writer.WriteAttributeString("Name", GetMemberName(memExp.Member));
                        this.Write(memExp.Expression);
                        this.m_writer.WriteEndElement();
                        return;
                    }
                    this.Write(memExp.Expression);
                    return;
                }
                case ExpressionType.Not:
                {
                    this.m_writer.WriteStartElement(s_opNames[exp.NodeType]);
                    UnaryExpression expression3 = (UnaryExpression) exp;
                    this.Write(expression3.Operand);
                    this.m_writer.WriteEndElement();
                    return;
                }
                case ExpressionType.Parameter:
                {
                    ParameterExpression expression9 = (ParameterExpression) exp;
                    this.m_writer.WriteStartElement("ExpressionParameter");
                    this.m_writer.WriteAttributeString("Name", expression9.Name);
                    this.m_writer.WriteEndElement();
                    return;
                }
                default:
                    throw new ClientRequestException(Resources.GetString("NotSupportedExpression", new object[] { this.m_condition.ToString() }));
            }
            if (!flag)
            {
                throw new ClientRequestException(Resources.GetString("NotSupportedExpression", new object[] { this.m_condition.ToString() }));
            }
            if (string.IsNullOrEmpty(str))
            {
                this.Write(expression4.Operand);
            }
            else
            {
                this.m_writer.WriteStartElement("ExpressionConvert");
                this.m_writer.WriteAttributeString("Type", str);
                this.Write(expression4.Operand);
                this.m_writer.WriteEndElement();
            }
        }

        private class NotSupportedExpressionChecker : Microsoft.SharePoint.Client.ExpressionVisitor
        {
            private Dictionary<string, Type> m_allowedParameters;

            public NotSupportedExpressionChecker(Dictionary<string, Type> allowedParameters)
            {
                this.m_allowedParameters = allowedParameters;
            }

            public override Expression VisitMemberAccess(MemberExpression m)
            {
                if ((!Attribute.IsDefined(m.Member, typeof(RemoteAttribute)) && !Attribute.IsDefined(m.Member, typeof(PseudoRemoteAttribute))) && (m.Expression != null))
                {
                    bool flag2 = false;
                    if (((m.Member.MemberType == MemberTypes.Property) && typeof(ClientRuntimeContext).IsAssignableFrom(m.Expression.Type)) && typeof(ClientObject).IsAssignableFrom(m.Type))
                    {
                        flag2 = true;
                    }
                    else if (ConditionalExpressionToXmlConverter.IsClientResultValueExpression(m))
                    {
                        flag2 = true;
                    }
                    else if (ConditionalExpressionToXmlConverter.IsServerObjectIsNullValue(m))
                    {
                        flag2 = true;
                    }
                    else if (m.Member.MemberType == MemberTypes.Field)
                    {
                        flag2 = true;
                    }
                    if (!flag2)
                    {
                        throw new ClientRequestException(Resources.GetString("NotSupportedMemberInExpression", new object[] { m.Member.Name }));
                    }
                }
                return base.VisitMemberAccess(m);
            }

            public override Expression VisitMethodCall(MethodCallExpression m)
            {
                if (!(Attribute.IsDefined(m.Method, typeof(RemoteAttribute)) || Attribute.IsDefined(m.Method, typeof(PseudoRemoteAttribute))))
                {
                    throw new ClientRequestException(Resources.GetString("NotSupportedMemberInExpression", new object[] { m.Method.Name }));
                }
                return base.VisitMethodCall(m);
            }

            public override Expression VisitParameter(ParameterExpression p)
            {
                if (((this.m_allowedParameters == null) || !this.m_allowedParameters.ContainsKey(p.Name)) || (this.m_allowedParameters[p.Name] != p.Type))
                {
                    throw new ClientRequestException(Resources.GetString("NotSupportedExpression", new object[] { p.ToString() }));
                }
                return base.VisitParameter(p);
            }
        }
    }
}

